import * as PIXI from 'pixi.js';
import Reel from './Reel';

// 默认导出卷轴列表构造函数的方法
export default class ReelsContainer {
    // 卷轴列表
    public readonly reels: Array<Reel> = [];
    // 创建卷轴列表的容器
    public readonly container: PIXI.Container;
    // 获胜模式(5 * 3)
    public readonly successMode: number[][] = [
        [2, 2, 2, 2, 2], [1, 1, 1, 1, 1], [3, 3, 3, 3, 3], [1, 2, 3, 2, 1], [3, 2, 1, 2, 3],
        [2, 1, 1, 1, 2], [2, 3, 3, 3, 2], [1, 1, 2, 3, 3], [3, 3, 2, 1, 1], [2, 3, 2, 1, 2],
        [2, 1, 2, 3, 2], [1, 2, 2, 2, 1], [3, 2, 2, 2, 3], [1, 2, 1, 2, 1], [3, 2, 3, 2, 3],
        [2, 2, 1, 2, 2], [2, 2, 3, 2, 2], [1, 1, 3, 1, 1], [3, 3, 1, 3, 3], [1, 3, 3, 3, 1],
        [3, 1, 1, 1, 3], [2, 3, 1, 3, 2], [2, 1, 3, 1, 2], [1, 3, 1, 3, 1], [3, 1, 3, 1, 3]
    ]

    constructor(app: PIXI.Application, hornSprite: PIXI.Container) {
        // 卷轴左侧偏移量
        // const REEL_OFFSET_LEFT = 70;

        // 单列卷轴数量
        const NUMBER_OF_REELS = 5;
        // 创建一个卷轴列表容器对象
        this.container = new PIXI.Container();

        // 根据设置的单列卷轴数量进行循环遍历
        for (let i = 0; i < NUMBER_OF_REELS; i++) {
            // 创建单列卷轴对象
            const reel = new Reel(app, i, hornSprite);
            // 将单列卷轴对象添加到卷轴列表中
            this.reels.push(reel);
            // 将单列卷轴容器添加卷轴列表容器中
            this.container.addChild(reel.container);
        }
        // 设置卷轴列表容器的横坐标
        // this.container.x = REEL_OFFSET_LEFT;
    }

    // 快速旋转的方法
    async spin() {
        // Overall time of spinning = shiftingDelay * this.reels.length
        // 卷轴旋转时间 = 换档延迟 * 这个卷轴的长度
        const shiftingDelay = 200;
        // 获取开始时间戳
        const start = Date.now();
        // 克隆一个新的卷轴列表
        const reelsToSpin = [...this.reels];
        // 执行无线旋转的方法(直到执行内部break语句为止)
        for await (let value of this.infiniteSpinning(reelsToSpin)) {
            // 转移等待时间 = (卷轴列表长度 - 新卷轴列表长度 + 1) * 卷轴旋转时间
            const shiftingWaitTime = (this.reels.length - reelsToSpin.length + 1) * shiftingDelay;
            // 如果 当前时间戳 >= 开始时间戳 + 转移等待时间
            if (Date.now() >= start + shiftingWaitTime) {
                // 删除新卷轴列表的第一个单列卷轴元素(停止这个单列卷轴的旋转)
                reelsToSpin.shift();
            }

            // 如果新的卷轴列表长度为0,则停止循环
            if (!reelsToSpin.length) break;
        }
        // reel.sprites[2] - Middle visible symbol of the reel
        // 将所有单列卷轴的第三行(页面显示的第二行)卷轴元素进行检查
        // return this.checkForWin(this.reels.map(reel => reel.sprites[2]));
        // 调用检查当前卷轴排列是否符合获胜模式的方法
        return this.checkSuccessMode();
    }

    // 无限旋转的方法(生成器函数在执行时能暂停，后面又能从暂停处继续执行。)
    private async* infiniteSpinning(reelsToSpin: Array<Reel>) {
        // 循环执行循环体内的语句
        while (true) {
            // 遍历新卷轴列表并拿到一个包含新卷轴列表中单列卷轴的旋转方法数组
            const spinningPromises = reelsToSpin.map(reel => reel.spinOneTime());
            // 同时执行所有单列卷轴旋转方法返回的回调函数
            await Promise.all(spinningPromises);
            // 调用更新新卷轴列表中所有单列卷轴第一个元素的方法
            this.blessRNG();
            yield;
        }
    }

    // 检查获胜模式的方法
    private checkSuccessMode(): number {
        // 遍历获胜模式
        const result = this.successMode.findIndex((mode, modeIndex) => {
            // 获取当前模式对应的卷轴元素
            const symbolsr = mode.map((eleNumber, i) => this.reels[i].sprites[eleNumber])
            // 检查当前模式是否符合
            return this.checkForWin(symbolsr)
        })
        // 返回遍历结果
        return result
    }

    // 检查是否获胜的方法
    private checkForWin(symbols: Array<PIXI.Sprite>): boolean {
        // Set of strings: 'SYM1', 'SYM2', ...
        // 创建一个Set对象
        const combination: Set<string> = new Set();
        // 遍历传入的卷轴元素列表(将卷轴元素的的纹理资源名称添加到Set对象中,简单来说就是通过图片名称判断卷轴元素类型)
        symbols.forEach(symbol => combination.add(symbol.texture.textureCacheIds[0].split('.')[0]));
        // 如果传入的卷轴元素类型都一样,并且没有赖子,则返回成功的执行结果
        if (combination.size === 1 && !combination.has('SYM1')) return true;
        // 如果传入的卷轴元素类型为两个,其中一个是卷轴的元素类型为赖子,则同样返回成功的执行结果
        return combination.size === 2 && combination.has('SYM1');
    }

    // 更新单列卷轴第一个元素的方法
    private blessRNG() {
        // 遍历卷轴列表中所有的单列卷轴
        this.reels.forEach(reel => {
            // 更新单列卷轴中第一个元素的纹理
            reel.sprites[0].texture = reel.textures[Math.floor(Math.random() * reel.textures.length)];
        });
    }
}
